package cn.sytton.taffe.web.flw.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.sytton.taffe.framework.base.entity.PageParam;
import cn.sytton.taffe.framework.base.service.BaseService;
import cn.sytton.taffe.framework.util.SecurityUtil;
import cn.sytton.taffe.web.flw.entity.*;
import cn.sytton.taffe.web.flw.help.constant;
import cn.sytton.taffe.web.flw.listener.ProcessListenerEntity;
import cn.sytton.taffe.web.flw.mapper.FormInstanceDataMapper;
import cn.sytton.taffe.web.flw.mapper.FormInstanceMapper;
import cn.sytton.taffe.web.flw.service.IFlowRunService;
import cn.sytton.taffe.web.flw.service.IFormDefService;
import cn.sytton.taffe.web.flw.service.IFormInstanceService;
import cn.sytton.taffe.web.sys.entity.DeptEntity;
import cn.sytton.taffe.web.sys.service.IDeptService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.github.pagehelper.PageInfo;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Timestamp;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 表单实例 服务实现类
 * </p>
 *
 * @author rock
 * @since 2023-09-27
 */
@Service
@RequiredArgsConstructor
@Transactional(rollbackFor = Exception.class)
public class FormInstanceService extends BaseService<FormInstanceMapper, FormInstanceEntity> implements IFormInstanceService {

    private final IFormDefService formDefService;
    private final FormInstanceDataMapper formInstanceDataMapper;
    private final IDeptService deptService;
    private final IFlowRunService flowRunService;

    @Override
    public FormInstanceEntity findById(String id) {
        FormInstanceEntity entity = super.findById(id);
        if (entity != null) {
            FormInstanceDataEntity dataEntity = formInstanceDataMapper.selectById(id);
            if (dataEntity != null) {
                entity.setFormJson(dataEntity.getFormJson());
                entity.setFormValue(dataEntity.getFormValue());
            }
        }
        return entity;
    }

    @Override
    public void start(FlowInstanceVariableEntity fiv) {
        //保存或更新主表
        FormInstanceEntity entity = fiv.getFormInstance();
        FormDefEntity defEntity =  formDefService.findById(entity.getFormId());
        entity.setApplyUserId(SecurityUtil.getUserId())
                .setApplyUserName(SecurityUtil.getUserName())
                .setApplyDeptName(getDeptName(SecurityUtil.getUserId()))
                .setApplyDateTime(new Date())
                .setFormInstanceCode(getNextFormInstanceCode(defEntity))
                .setProcessDefId(defEntity.getProcessDefId())
                .setProcessDefKey(defEntity.getProcessDefKey());
        saveOrUpdate(entity);

        //保存或更新实例数据
        FormInstanceDataEntity dataEntity = formInstanceDataMapper.selectById(entity.getFormInstanceId());
        if (dataEntity == null) {
            dataEntity = new FormInstanceDataEntity()
                    .setFormInstanceId(entity.getFormInstanceId())
                    .setFormJson(entity.getFormJson())
                    .setFormValue(entity.getFormValue());
            formInstanceDataMapper.insert(dataEntity);
        } else {
            dataEntity.setFormJson(entity.getFormJson())
                    .setFormValue(entity.getFormValue());
            formInstanceDataMapper.updateById(dataEntity);
        }

        // 启动流程
        String processInstanceId = flowRunService.start(fiv, defEntity.getProcessDefKey(), defEntity.getProcessDefId());

        // 更新主表实例,状态
        if (StringUtils.isNoneEmpty(processInstanceId)) {
            updateWithUpdateField(new LambdaUpdateWrapper<FormInstanceEntity>()
                    .set(FormInstanceEntity::getProcessInstanceId, processInstanceId)
                    .set(FormInstanceEntity::getStatue, constant.FLOW_STATUS.ING.getVal())
                    .eq(FormInstanceEntity::getFormInstanceId, entity.getFormInstanceId()));
        }
    }

    private synchronized String getNextFormInstanceCode(FormDefEntity defEntity) {
        //多个微服务部署时不能这样用synchronized
        String todayStr = DateUtil.formatDate(new Date());
        Date today = DateUtil.parseDate(todayStr + " 00:00:00 000");
        List<FormInstanceEntity> entities = list(new LambdaQueryWrapper<FormInstanceEntity>()
                .eq(FormInstanceEntity::getFormId, defEntity.getFormId())
                .gt(FormInstanceEntity::getCreatedTime, today)
                .orderByDesc(FormInstanceEntity::getCreatedTime));
        if (entities != null && entities.size() == 0) {
            return defEntity.getFormCode() + "-" + todayStr.replaceAll("-","") + "-00001";
        } else {
            assert entities != null;
            int next = 1;
            String[] tmp = entities.get(0).getFormInstanceCode().split("-");
            if (tmp.length > 0) {
                next = Integer.parseInt(tmp[tmp.length -1]) + 1;
            }
            return defEntity.getFormCode() + "-" + todayStr.replaceAll("-","") + "-" + StringUtils.leftPad(String.valueOf(next), 5, "0");
        }
    }

    @Override
    public void approve(FlowTaskVariableEntity ftv) {
        formInstanceDataMapper.update(null,
                new LambdaUpdateWrapper<FormInstanceDataEntity>()
                        .set(FormInstanceDataEntity::getFormValue, ftv.getFormInstance().getFormValue())
                        .set(FormInstanceDataEntity::getUpdatedTime, new Timestamp(new Date().getTime()))
                        .set(FormInstanceDataEntity::getUpdatedUserId, SecurityUtil.getUserId())
                        .eq(FormInstanceDataEntity::getFormInstanceId, ftv.getFormInstance().getFormInstanceId()));
        flowRunService.complete(ftv);
    }

    @Override
    public void flowListener(ProcessListenerEntity le) {
        if (le.getActivityType().equals(constant.ACTIVITY_TYPE.TASK)) {
            if (le.getEvent().equals(constant.FLOW_LISTENER_EVENT.START)) {
                updateWithUpdateField(new LambdaUpdateWrapper<FormInstanceEntity>()
                        .set(FormInstanceEntity::getCurrentTask, le.getTaskName())
                        .eq(FormInstanceEntity::getFormInstanceId, le.getBusinessId()));
            }
        } else if (le.getActivityType().equals(constant.ACTIVITY_TYPE.END)) {
            int status = constant.FLOW_STATUS.COMPLETED.getVal();
            if (le.getVariables().containsKey("pass")) {
                status = Boolean.parseBoolean(le.getVariables().get("pass").toString())
                        ? constant.FLOW_STATUS.PASS.getVal()
                        : constant.FLOW_STATUS.REJECT.getVal();
            }
            updateWithUpdateField(new LambdaUpdateWrapper<FormInstanceEntity>()
                    .set(FormInstanceEntity::getCompletedDateTime, new Date())
                    .set(FormInstanceEntity::getStatue, status)
                    .set(FormInstanceEntity::getCurrentTask, "结束")
                    .eq(FormInstanceEntity::getFormInstanceId, le.getBusinessId()));
        }
    }

    @Override
    public PageInfo<FormInstanceEntity> findMyApplyPage(PageParam pp) {
        pp.getParams().put("applyUserId", SecurityUtil.getUserId());
        return findPage(pp);
    }

    @Override
    public PageInfo<FormInstanceEntity> findMyApprovePage(PageParam pp) {
        pp.getParams().put("approveUserId", SecurityUtil.getUserId());
        setPageParams(pp);
        return new PageInfo<>(baseMapper.findApproveList(pp.getParams()));
    }

    private String getDeptName(String userId) {
        List<DeptEntity> depts = deptService.findByUserId(userId);
        List<String> deptNames = depts.stream().map(DeptEntity::getDeptName).collect(Collectors.toList());
        return StringUtils.join(deptNames, "|");
    }
}
